import java.math.BigDecimal;
import java.util.*;

/**
 * @Author : Chris Wang
 * @Description : 位置工具类
 * @Date : 2019/7/1 14:55
 */
//@Log4j2
public class LocationUtil {

    private static final double x_PI = 3.14159265358979324 * 3000.0 / 180.0;
    private static final BigDecimal KM = new BigDecimal("1000");
    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    public static double pi = 3.1415926535897932384626;
    public static double a = 6378245.0;
    public static double ee = 0.00669342162296594323;

    /********************1、高德经纬度用的是火星坐标系  ******************************************/

    /**
     * 火星转百度 坐标
     */
    public static Map<String, Double> gcj02Tobd(String ggLat, String ggLng) {
        if (isEmpty(ggLat, ggLng)) {
            return null;
        }
        double lng = Double.parseDouble(ggLng);
        double lat = Double.parseDouble(ggLat);
        double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
        double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
        double bdLng = z * Math.cos(theta) + 0.0065;
        double bdLat = z * Math.sin(theta) + 0.006;
        return convertResult(bdLat, bdLng);
    }

    /**
     * 高德转百度 坐标
     */
    public static Map<String, Double> gdToBd(String gg_lat, String gg_lon) {
        if (isEmpty(gg_lat, gg_lon)) {
            return null;
        }
        double x = Double.valueOf(gg_lon).doubleValue(), y = Double.valueOf(gg_lat).doubleValue();
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;

        return convertResult(Double.valueOf(String.valueOf(bd_lat)), Double.valueOf(String.valueOf(bd_lon)));
    }

    /**
     * 百度转高德 坐标
     */
    public static Map<String, Double> bdToGd(String bd_lat, String bd_lon) {
        if (bd_lat == null || "".equals(bd_lat) || "null".equals(bd_lat) ||
                bd_lon == null || "".equals(bd_lon) || "null".equals(bd_lon)) {
            return null;
        }
        double x = Double.valueOf(bd_lon).doubleValue() - 0.0065, y = Double.valueOf(bd_lat).doubleValue() - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gg_lon = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        Map<String, Double> data = new HashMap<String, Double>();
        data.put("lon", Double.valueOf(String.valueOf(gg_lon)));
        data.put("lat", Double.valueOf(String.valueOf(gg_lat)));
        return data;
    }

    /**
     * 84坐标系 转 百度
     */
    public static double[] gps84_To_bd09(String lat, String lon) {
        if (isEmpty(lat, lon)) {
            return null;
        }
        Map<String, Double> gc = gps84_To_Gcj02(lat, lon);
        return gcj02_To_Bd09(gc.get("lat"), gc.get("log"));
    }

    /**
     * 百度 转 84坐标系
     */
    public static Map<String, Double> bd09_To_gps84(String gLat, String gLon) {
        if (isEmpty(gLat, gLon)) {
            return null;
        }
        Map<String, Double> gc = bd09_To_Gcj02(gLat, gLat);
        Map<String, Double> _84 = gcj02_To_Gps84(gc.get("lat").toString(), gc.get("log").toString());

        //保留小数点后六位  
        Map<String, Double> result = new HashMap<String, Double>();
        result.put("lat", retain6(_84.get("lat")));
        result.put("lon", retain6(_84.get("lon")));
        return result;
    }

    /**
     * 火星坐标系 (GCJ-02) 转 百度坐标系 (BD-09)
     * GCJ-02 坐标转换成 BD-09 坐标
     */
    public static double[] gcj02_To_Bd09(double lat, double lon) {
        double x = lon, y = lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double tempLon = z * Math.cos(theta) + 0.0065;
        double tempLat = z * Math.sin(theta) + 0.006;
        double[] gps = {tempLat, tempLon};
        return gps;
    }

    /**
     * 百度坐标系 (BD-09)  转 火星坐标系 (GCJ-02)
     */
    public static Map<String, Double> bd09_To_Gcj02(String gLat, String gLon) {
        if (isEmpty(gLat, gLon)) {
            return null;
        }
        double lat = strToDou(gLat), lon = strToDou(gLon);

        double x = lon - 0.0065, y = lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double tempLon = z * Math.cos(theta);
        double tempLat = z * Math.sin(theta);
        return convertResult(tempLat, tempLon);
    }

    /**
     * 84坐标系 to 火星坐标系 (GCJ-02)
     */
    public static Map<String, Double> gps84_To_Gcj02(String gLat, String gLon) {
        if (isEmpty(gLat, gLon)) {
            return null;
        }
        double lat = strToDou(gLat), lon = strToDou(gLon);


        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return convertResult(mgLat, mgLon);
    }

    /**
     * 火星坐标系 (GCJ-02) 转 84坐标系
     */
    public static Map<String, Double> gcj02_To_Gps84(String gLat, String gLon) {
        if (isEmpty(gLat, gLon)) {
            return null;
        }
        double lat = strToDou(gLat), lon = strToDou(gLon);
        double[] gps = transform(lat, lon);
        double lontitude = lon * 2 - gps[1];
        double latitude = lat * 2 - gps[0];
        return convertResult(latitude, lontitude);
    }

    private static double strToDou(String num) {
        return Double.valueOf(num);
    }

    private static Map<String, Double> convertResult(double lat, double lon) {
        Map<String, Double> data = new HashMap<String, Double>();
        data.put("lat", Double.valueOf(String.valueOf(lat)));
        data.put("lon", Double.valueOf(String.valueOf(lon)));
        return data;
    }

    private static boolean isEmpty(String ggLng, String ggLat) {
        if (ggLng == null || "".equals(ggLng) || "null".equals(ggLng) ||
                ggLat == null || "".equals(ggLat) || "null".equals(ggLat)) {
            return true;
        }
        return false;
    }

    /**
     * 保留小数点后六位
     *
     * @param num
     * @return
     */
    private static double retain6(double num) {
        String result = String.format("%.6f", num);
        return Double.valueOf(result);
    }

    public static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    public static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    public static double[] transform(double lat, double lon) {
        if (outOfChina(lat, lon)) {
            return new double[]{lat, lon};
        }
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double mgLat = lat + dLat;
        double mgLon = lon + dLon;
        return new double[]{mgLat, mgLon};
    }

    public static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }
}
